#!/usr/bin/env python
# -*- coding:utf8 -*-
# uncompyle6 version 2.15.1
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.10 (default, Jul  1 2017, 13:36:56) 
# [GCC 4.4.6 20110731 (Red Hat 4.4.6-4)]
# Embedded file name: ./paas/engine/deploy.py
# Compiled at: 2017-11-16 15:44:28
"""
Tencent is pleased to support the open source community by making 蓝鲸智云(BlueKing) available.
Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.

app engine 部署相关

# NOTE: 异常统一在这一层处理

Copyright © 2012-2017 Tencent BlueKing. All Rights Reserved. 蓝鲸智云 版权所有
"""
import json
from collections import defaultdict
from django.conf import settings
from common.log import logger
from engine.api import release_app, unrelease_app
from engine.models import ThirdServer, THIRD_SERVER_CATEGORY_MQ
from app.models import App, SecureInfo
from app.constants import AppStateEnum
from release.models import Record, Version
from release.constants import OperateIDEnum, StatusEnum, DEPLOY_ERROR_DICT
from saas.utils import get_saas_settings_for_deploy

def _get_app_deploy_vars(app_code):
    """
    deploy_vars: 目前只有 vcs 版本管理信息
    """
    info = SecureInfo.objects.filter(app_code=app_code)
    if not info:
        return {}
    info = info[0]
    return {'VCS_TYPE': info.vcs_type_text,'VCS_PATH': info.vcs_url,
       'VCS_USERNAME': info.vcs_username,
       'VCS_PASSWORD': info.vcs_password
       }


def _get_app_env(app_code, is_with_db_info=False):
    """
    envs
    
    NOTE: 注意, envs类型默认都是字符串
    """
    app = App.objects.get(code=app_code)
    info = SecureInfo.objects.filter(app_code=app_code)
    if not info:
        return {}
    bk_paas_host = 'http://%s' % settings.PAAS_DOMAIN
    cc_host = 'http://%s' % settings.HOST_CC
    job_host = 'http://%s' % settings.HOST_JOB
    info = info[0]
    envs = {'APP_ID': info.app_code,'APP_TOKEN': app.auth_token,
       'BK_PAAS_HOST': bk_paas_host,
       'BK_CC_HOST': cc_host,
       'BK_JOB_HOST': job_host
       }
    if is_with_db_info:
        envs.update({'DB_TYPE': info.db_type,
           'DB_HOST': info.db_host,
           'DB_PORT': info.db_port,
           'DB_NAME': info.db_name,
           'DB_USERNAME': info.db_username,
           'DB_PASSWORD': info.db_password
           })
    for key, value in envs.iteritems():
        envs[key] = str(value)

    return envs


def _get_services():
    """
    获取所有注册的服务
    """
    services = defaultdict(list)
    third_server_list = ThirdServer.objects.filter(category=THIRD_SERVER_CATEGORY_MQ, is_active=True).all()
    for server in third_server_list:
        services[server.category].append(server.id)

    return services


def _make_record(app_code, app_old_state, operate_user, operate_id, is_success):
    record = Record.objects.create_record(app_code=app_code, app_old_state=app_old_state, operate_user=operate_user, operate_id=operate_id, is_success=is_success)
    return record


def _save_record(record, event_id=None, operate_id=None, message=None, is_tips=None, is_success=None, extra_data=None):
    if event_id is not None:
        record.event_id = event_id
    if extra_data is not None:
        record.extra_data = extra_data
    if operate_id is not None:
        record.operate_id = operate_id
    if message is not None:
        record.message = message
    if is_tips is not None:
        record.is_tips = is_tips
    if is_success is not None:
        record.is_success = is_success
    record.save()
    return


def _unpack_release_result(result):
    """
    对release_app的参数拆包
    """
    event_id = result.get('event_id') if result else None
    error_code = result.get('error_code', 0) if result else 0
    error_msg = result.get('msg', '') if result else ''
    return (
     event_id, error_code, error_msg)


def _build_relase_data(app, mode, is_saas=None, saas_settings=None):
    """
    构造release_app中data字段的数据结构
    """
    app_code = app.code
    deploy_token = app.deploy_token
    deploy_vars = _get_app_deploy_vars(app_code)
    is_with_db_info = is_saas
    envs = _get_app_env(app_code, is_with_db_info=is_with_db_info)
    triggers = dict(is_use_celery=app.is_use_celery, is_use_celery_beat=app.is_use_celery_beat)
    services = _get_services()
    data = dict(envs=envs, triggers=triggers, services=services, mode=mode, deploy_token=deploy_token, deploy_vars=deploy_vars)
    if is_saas is not None:
        data['is_saas'] = is_saas
    if saas_settings is not None:
        data['saas_settings'] = saas_settings
    return data


def app_to_test_task(app_code, app, username):
    """
    提测后台任务
    """
    app_old_state = app.state
    try:
        record = _make_record(app_code, app_old_state, username, OperateIDEnum.IN_TEST, StatusEnum.FAIL)
        App.objects.filter(code=app_code).update(state=AppStateEnum.IN_TEST)
        auth_token = app.auth_token
        data = _build_relase_data(app, mode='test')
        is_success, result = release_app(app_code=app_code, auth_token=auth_token, data=data)
        event_id, error_code, error_msg = _unpack_release_result(result)
        if not is_success:
            if error_code:
                error_msg = DEPLOY_ERROR_DICT.get(str(error_code))
            else:
                error_msg = u'测试部署事件提交失败！请确认 1.appengine服务正常 2.安装并注册了对应的agent服务器 3.第三方服务等正常 [%s]' % error_msg
            msg = u'[app:%s] %s, error_code: %s, error_msg: %s' % (app_code, u'测试部署事件提交失败', error_code, error_msg)
            logger.info(msg)
            App.objects.filter(code=app_code).update(state=app_old_state)
            _save_record(record=record, operate_id=OperateIDEnum.TO_TEST, message=msg, is_success=False)
            return (
             False, None, u'测试部署事件提交失败, %s' % error_msg)
        msg = u'[app:%s] %s' % (app_code, u'测试部署事件提交成功')
        logger.info(msg)
        _save_record(record, event_id=event_id, message=msg, extra_data=json.dumps({'task_detail': '','event_id': event_id}))
        return (
         True, event_id, None)
    except Exception as e:
        logger.info(u'[app:%s] %s' % (app_code, u'测试部署事件提交失败'))
        msg = u'[app:%s] 提测任务失败，error：%s' % (app_code, e)
        logger.exception(msg)
        App.objects.filter(code=app_code).update(state=app_old_state)
        _save_record(record, operate_id=OperateIDEnum.TO_TEST, message=msg, is_success=False)
        return (
         False, None, u'测试部署事件提交失败, %s' % str(e))

    return


def app_to_online_task(app_code, app, username, is_tips, features, bugs):
    """
    上线操作后台任务
    """
    app_old_state = app.state
    try:
        record = _make_record(app_code, app_old_state, username, OperateIDEnum.IN_ONLINE, StatusEnum.FAIL)
        App.objects.filter(code=app_code).update(state=AppStateEnum.IN_ONLINE)
        auth_token = app.auth_token
        data = _build_relase_data(app, mode='prod')
        is_success, result = release_app(app_code=app_code, auth_token=auth_token, data=data)
        event_id, error_code, error_msg = _unpack_release_result(result)
        if not is_success:
            if error_code:
                error_msg = DEPLOY_ERROR_DICT.get(str(error_code))
            else:
                error_msg = u'正式部署事件提交失败！请确认 1.appengine服务正常 2.安装并注册了对应的agent服务器 3.第三方服务等正常 [%s]' % error_msg
            msg = u'[app:%s] %s, error_code: %s, error_msg: %s' % (app_code, u'上线部署事件提交失败', error_code, error_msg)
            logger.info(msg)
            App.objects.filter(code=app_code).update(state=app_old_state)
            _save_record(record=record, operate_id=OperateIDEnum.TO_ONLINE, message=msg, is_success=False)
            return (
             False, None, u'上线部署事件提交失败, %s' % error_msg)
        msg = u'[app:%s] %s' % (app_code, u'上线部署事件提交成功')
        logger.info(msg)
        Version.objects.create_version(app, bugs, features, username)
        try:
            extra_data = json.loads(record.extra_data)
            extra_data['task_detail'] = ''
            extra_data['event_id'] = event_id
        except Exception as e:
            extra_data = {'task_detail': '','event_id': event_id}

        _save_record(record, event_id=event_id, message=msg, is_tips=int(is_tips), extra_data=json.dumps(extra_data))
        return (
         is_success, event_id, None)
    except Exception as e:
        logger.info(u'[app:%s] %s' % (app_code, u'上线部署事件提交失败'))
        msg = u'[app:%s] app上线任务执行发生异常, error: %s' % (app_code, e)
        logger.exception(msg)
        App.objects.filter(code=app_code).update(state=app_old_state)
        _save_record(record, operate_id=OperateIDEnum.TO_ONLINE, message=msg, is_success=False)
        return (
         False, None, u'上线部署事件提交失败, %s' % str(e))

    return


def app_to_outline_task(app_code, auth_token, username, mode, app_old_state):
    """
    下架操作后台任务
    """
    try:
        record = _make_record(app_code, app_old_state, username, OperateIDEnum.IN_OUTLINE, StatusEnum.FAIL)
        App.objects.filter(code=app_code).update(state=AppStateEnum.IN_OUTLINE)
        is_success, event_ids = unrelease_app(app_code, auth_token, mode)
        if not is_success:
            msg = u'[app:%s] %s' % (app_code, u'下架事件提交失败')
            logger.info(msg)
            App.objects.filter(code=app_code).update(state=app_old_state)
            _save_record(record=record, operate_id=OperateIDEnum.TO_OUTLINE, message=msg, is_success=False)
            return (
             False, None)
        msg = u'[app:%s] %s' % (app_code, u'下架事件提交成功')
        logger.info(msg)
        extra_data = {'mode': mode,'task_detail': '','event_ids': event_ids}
        _save_record(record, event_id=event_ids[0], message=msg, extra_data=json.dumps(extra_data))
        return (
         is_success, event_ids[0])
    except Exception as e:
        logger.info(u'[app:%s] %s' % (app_code, u'下架事件提交失败'))
        msg = u'[app:%s] 下架任务失败 error：%s' % (app_code, e)
        logger.exception(msg)
        App.objects.filter(code=app_code).update(state=app_old_state)
        _save_record(record, operate_id=OperateIDEnum.TO_OUTLINE, message=msg, is_success=False)
        return (
         False, None)

    return


def saas_app_to_online_task(saas_app_version, username):
    """
    上线操作后台任务
    """
    saas_app = saas_app_version.saas_app
    app = saas_app.app
    app_code = app.code
    app_old_state = app.state
    try:
        bugs = ''
        features = ''
        is_tips = '1'
        record = _make_record(app_code, app_old_state, username, OperateIDEnum.IN_ONLINE, StatusEnum.FAIL)
        App.objects.filter(code=app_code).update(state=AppStateEnum.IN_ONLINE)
        auth_token = app.auth_token
        saas_settings = get_saas_settings_for_deploy(saas_app_version)
        data = _build_relase_data(app, mode='prod', is_saas=True, saas_settings=saas_settings)
        is_success, result = release_app(app_code=app_code, auth_token=auth_token, data=data)
        event_id, error_code, error_msg = _unpack_release_result(result)
        if not is_success:
            if error_code:
                error_msg = DEPLOY_ERROR_DICT.get(str(error_code))
            else:
                error_msg = u'测试部署事件提交失败！请确认 1.appengine服务正常 2.安装并注册了对应的agent服务器 3.第三方服务等正常 [%s]' % error_msg
            msg = u'SaaS App [app:%s] %s, error_code: %s, error_msg: %s' % (app_code,
             u'SaaS App 上线部署事件提交失败',
             error_code,
             error_msg)
            logger.info(msg)
            App.objects.filter(code=app_code).update(state=app_old_state)
            _save_record(record=record, operate_id=OperateIDEnum.TO_ONLINE, message=msg, is_success=False)
            return (
             False, None, u'SaaS App 上线部署事件提交失败, %s' % error_msg)
        msg = u'[app:%s] %s' % (app_code, u'SaaS App 上线部署事件提交成功')
        logger.info(msg)
        Version.objects.create_version(app, bugs, features, username)
        try:
            extra_data = json.loads(record.extra_data)
            extra_data['task_detail'] = ''
            extra_data['event_id'] = event_id
        except Exception as e:
            extra_data = {'task_detail': '','event_id': event_id}

        _save_record(record, event_id=event_id, message=msg, is_tips=int(is_tips), extra_data=json.dumps(extra_data))
        return (
         is_success, event_id, None)
    except Exception as e:
        logger.info(u'[app:%s] %s' % (app_code, u'SaaS App 上线部署事件提交失败'))
        msg = u'SaaS App [app:%s] app上线任务执行发生异常, error: %s' % (app_code, e)
        logger.exception(msg)
        App.objects.filter(code=app_code).update(state=app_old_state)
        _save_record(record, operate_id=OperateIDEnum.TO_ONLINE, message=msg, is_success=False)
        return (
         False, None, u'SaaS App 上线部署事件提交失败, %s' % str(e))

    return
